// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include <stdio.h>
#include <iostream>
//#include "ipt.h"
#ifdef CHANGED
#include "synch.h"
#include "addrspace.h"
#include <map>
#include <sstream>
#endif

extern "C" {int bzero(char*, int);};

using namespace std;
   
#ifdef CHANGED
map <int, Lock*> lockMap;
map <int, Condition*> conditionMap;
map <int, AddrSpace*> lockSpace;
map <int, AddrSpace*> condSpace;
map <int, char*> lockNameMap;
map <int, int> lockStatus;

Lock *lockExec = new Lock("Lock Exec");
Lock *lockConsoleDisplay = new Lock("Lock Console Display");
Lock* createLockSysCallLock = new Lock("CreateLock Lock");
Lock* acquireSysCallLock = new Lock("Acquire Lock");
Lock* releaseSysCallLock = new Lock("Release Lock");
Lock* destroyLockSysCallLock = new Lock("DestroyLock Lock");
Lock* checkSysCallLock = new Lock("CheckLock Lock");
Lock* createConditionSysCallLock = new Lock("CreateCVLock Lock");
Lock* signalSysCallLock = new Lock("Signal Lock");
Lock* broadcastSysCallLock = new Lock("Broadcast Lock");
Lock* waitSysCallLock = new Lock("Wait Lock");
Lock* destroyConditionSysCallLock = new Lock("Destroy Lock");
Lock* yieldSysCallLock = new Lock("Yield Lock");
Lock* iptLock = new Lock("IPT Lock");
Lock* tlbLock = new Lock("TLB Lock");
#endif
#define maxlock 8000 //maximum # of locks a user can create
#define maxcondition 8000   //maximum # of CVs a user can create

bool unusedpage = false;

void CopyInfo(TranslationEntry* source,TranslationEntry* destination){
  //Copy from one locaiton to the other
  //Used to copy to/from TLB and to/from IPT

  destination->virtualPage = source->virtualPage;
  destination->physicalPage = source->physicalPage;
  destination->valid = source->valid;
  destination->use = source->use;
  destination->dirty = source->dirty;
  destination->readOnly = source->readOnly;  
}

//used to print TLB values if needed. 
void printTLB(int vp){
printf("virtual page %d\n",machine->tlb[vp].virtualPage);
printf("physical page %d\n",machine->tlb[vp].physicalPage);
printf("valid bit %d\n",machine->tlb[vp].valid);
printf("use bit %d\n",machine->tlb[vp].use);
printf("dirty bit %d\n",machine->tlb[vp].dirty);
printf("readOnly bit %d\n\n\n",machine->tlb[vp].readOnly);
}

//used in exit_syscall to invalidate ipt
void resetIPT(int i){
iptLock->Acquire();
	  IPT[i].virtualPage = -1;
	  IPT[i].physicalPage = -1;
	  IPT[i].valid = false;
	  IPT[i].use = false;
	  IPT[i].dirty = false;
	  IPT[i].readOnly = false; 
	  IPT[i].ProcessID = 0;
	  IPT[i].pagestatus= udata;
	  IPT[i].isEmpty = false;
iptLock->Release();
}


void HandlePageFault(int BadVAddr){

AddrSpace *space;//current address space
int vpn = BadVAddr/PageSize; //virtual page number
int ppn = -1; //physical page number
space = currentThread->space;
static int nextTLB = -1;

if(vpn > (space->numPages)){//make sure that the page is within legal range
	interrupt->Halt();
}

//Find next TLB location
nextTLB=(nextTLB+1)%TLBSize;


//check if the needed page is in IPT
//iptLock->Acquire();
for (int i=0; i<NumPhysPages; i++){
  // 1. If the entry in the TLB that is to be replaced is valid and dirty, propagate the dirty bit back to the IPT.
  // 2. Copy the information about the needed page into the TLB (virtual page, physical page).
  // 3. Set the TLB entry's valid bit to true.
	if(IPT[i].virtualPage == vpn && IPT[i].valid && space->getID() == IPT[i].ProcessID ){
		tlbLock->Acquire();
		if(machine->tlb[nextTLB].valid && machine->tlb[nextTLB].dirty){
			IPT[machine->tlb[nextTLB].physicalPage].dirty = TRUE; 
		} 
		tlbLock->Release();
		ppn = i;//found physical page number
		
		
	}
//iptLock->Release();
}
//on IPT miss

if(ppn == -1){ //phys page was not found in IPT
	
	ppn=pageBitMapTable->Find(); //find available page in memory (there is one for sure, because mem is unlimited)
	if(space->extpagetable[vpn].pagestatus==exec){//if it is in executable
	//now read it from executable into avail page in memory
	space->execFile->ReadAt(&(machine->mainMemory[ppn*PageSize]),PageSize,space->extpagetable[vpn].ExecLoc);
}
//update IPT
iptLock->Acquire();
	  IPT[ppn].virtualPage = vpn;
	  IPT[ppn].physicalPage = ppn;
	  IPT[ppn].valid = true;
	  IPT[ppn].use = false;
	  IPT[ppn].dirty = false;
	  IPT[ppn].readOnly = false; 
	  IPT[ppn].ProcessID = space->getID();
	  IPT[ppn].pagestatus= space->extpagetable[vpn].pagestatus;
	  IPT[ppn].space = space;
	  IPT[ppn].isEmpty = false;	
iptLock->Release();
}
	//if the bit is valid and dirty propagate dirty bit back to ipt
	if(machine->tlb[nextTLB].valid && machine->tlb[nextTLB].dirty){
		IPT[machine->tlb[nextTLB].physicalPage].dirty = TRUE; 
	}
tlbLock->Acquire();
//update TLB with needed page
	CopyInfo(&(IPT[ppn]),&(machine->tlb[nextTLB]));
	machine->tlb[nextTLB].valid = TRUE;
tlbLock->Release();

//end of HandlePageFault
}

	


int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) {
#ifndef USE_TLB
      result = machine->ReadMem( vaddr, 1, paddr );
      buf[n++] = *paddr;

      if ( !result ) {
	//translation failed
	return -1;
      }
#endif
#ifdef USE_TLB
      while(!machine->ReadMem(vaddr,1,paddr));
      buf[n++] = *paddr;	
#endif
      vaddr++;

    }

    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
#ifndef USE_TLB
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }
#endif
#ifdef USE_TLB
      n++;
      while(!machine->WriteMem( vaddr, 1, (int)(buf[n]) ));	
#endif
      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }
		
		buf[len] = '\0';
    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
				printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}

#ifdef CHANGED


/* Part one changes begin*/

void Yield_Syscall(int dispOption){
//Yield current thread
yieldSysCallLock->Acquire();

if (dispOption == 1)
		printf("Yield : %s\n", currentThread->getName());

currentThread->Yield();
yieldSysCallLock->Release();
}


/*  Project 3 updates Locks and CVs start*/

int CreateLock_Syscall(int vaddr){

	#ifdef NETWORK
	printf("+++++++in CreateLock_Syscall through network+++++++++\n");//100100

	char* buf = new char[32]; 
	//if buf is too big, error occurs.
	if (!buf) { 
		printf("ERROR: CreateLock_Syscall('buf' does not have enough memory.)");
		return -1; 
	}
	
	//copy the name of the Lock to buf
    if(copyin(vaddr, 32, buf)==-1) {
		printf("Error occurs in CreateLock_Syscall. Bad pointer passed\n");
		delete[] buf;
		return -1;
	}
  
   //send a message to the server 
   PacketHeader outPktHdr, inPktHdr; 
   MailHeader outMailHdr, inMailHdr; 
   char *msg = new char[32]; 
   char buffer[MaxMailSize]; 
   outPktHdr.to = 0; 
   outMailHdr.to = 0; 
  
   outMailHdr.from = netname; 

   //concatenate message with name 
   std::stringstream temp;

   temp << "1" << buf; 
   temp >> msg; 

   outMailHdr.length = strlen(msg) +1; 
   //send "CREATELOCK" 
   printf("in create syscall\n"); 
   bool success = postOffice->Send(outPktHdr, outMailHdr, msg); 
   if ( !success ) { 
     printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n"); 
     interrupt->Halt(); 
   } 

   //Wait for server's reply 
   postOffice->Receive(netname, &inPktHdr, &inMailHdr, buffer); 
   printf("Lock%s is created by server\n", buffer); 
   fflush(stdout); 

   //check an error
	int result;
	sscanf(buffer,"%d",&result);
	if (result == -1) {
		printf("Error occurs in CreateLock_Syscall with lock%s", buf);
		return -1;
	}
   //return lock number
	 return  atoi(buffer);
	#endif

	#ifndef NETWORK 
     createLockSysCallLock->Acquire();
     //Create Lock system call
     static int lockIndex = 0;
     static int  overindex=0;
     if(lockIndex<=(maxlock-1)){	
     if (lockIndex>=0){		
     char* lockName = new char[15];

     Lock *lock;	
     lock = new Lock(lockName); 
    /* Store Lock object in the Lock Map */
     lockMap[lockIndex] = lock;
    
    /* Store Lock object in the Lock Name Map */
     lockNameMap[lockIndex] = lockName;
     
    /* Store the lock status*/
     lockStatus[lockIndex] = 0; // Zero for unlocked Lock, One otherwise

     printf("Lock %d was created\n", lockIndex);
    
     createLockSysCallLock->Release();	

     return lockIndex++; 
     }}

	printf("lock was not created. maximum number of locks %d  exceeded \n", maxlock);
	createLockSysCallLock->Release();
	return -1;
	#endif 

}
int CreateCondition_Syscall(int vaddr){
 #ifdef NETWORK 
	printf("+++in CreateCondition_Syscall through network++++\n");//100100
  
   char* buf; 
   buf = new char[32]; 
   //read in the string at vaddr, copy it to buf 
   copyin(vaddr, 32, buf); 
  
 //Send a message to the server 
   PacketHeader outPktHdr, inPktHdr; 
   MailHeader outMailHdr, inMailHdr; 
   char *msg = "2"; 
   char buffer[MaxMailSize]; 
   outPktHdr.to = 0; 
   outMailHdr.to = 0; 
   outMailHdr.from = netname; 
  
   //concatenate strings 
   strcat(msg, buf); 
   outMailHdr.length = strlen(msg) +1; 
  
   //Send request "CREATECV" 
   bool success = postOffice->Send(outPktHdr, outMailHdr, msg); 
   if ( !success ) { 
     printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n"); 
     interrupt->Halt(); 
   } 
  
   //Wait for the server's response 
   postOffice->Receive(netname, &inPktHdr, &inMailHdr, buffer); 
   printf("CV %s created by server\n", buffer); 
   fflush(stdout); 
  
   //return the index to the calling program 
   int returnValue = atoi(buffer); 
   return returnValue; 
 #endif 

	#ifndef NETWORK 	
	createConditionSysCallLock->Acquire();
    //creating conditin system call

    static int conditionIndex = 0;
    if(conditionIndex<=(maxcondition-1)){	
    if (conditionIndex>=0){	
    char* conditionName = new char[15];	
    
    conditionMap[conditionIndex] = new Condition(conditionName); //create and store new condition in the condition map 	
    printf("Condition %d was created\n", conditionIndex);
    createConditionSysCallLock->Release();
    return conditionIndex++;	
    }}
   
	printf("condition was not created. maximum number of conditions %d exceeded \n", maxcondition);
	createConditionSysCallLock->Release();
	return -1;
    #endif 
}

int Acquire_Syscall(int lockIndex){
//int Acquire_Syscall(){

	#ifdef NETWORK
	
	printf("+++++++in Acquire_Syscall through network+++++++++\n");//100100

   //prepare to send a message to the server 
   PacketHeader outPktHdr, inPktHdr; 
   MailHeader outMailHdr, inMailHdr; 
   char *msg = "3"; 
   char buffer[MaxMailSize]; 
   outPktHdr.to = 0; 
   outMailHdr.to = 0; 
   outMailHdr.from = netname; 
  
   std::stringstream temp; 
   temp << lockIndex; 
   std::string tempStr; 
   tempStr = temp.str(); 
   strcat(msg, tempStr.c_str()); 
   outMailHdr.length = strlen(msg) +1; 
  
   //send request, Acquire_Syscall to server
   bool success = postOffice->Send(outPktHdr, outMailHdr, msg); 
   if ( !success ) { 
     printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n"); 
     interrupt->Halt(); 
   } 
  
   //Wait for reply from server
   postOffice->Receive(netname, &inPktHdr, &inMailHdr, buffer); 
   printf("Lock %s acquired by server\n", buffer); 
   fflush(stdout); 

	int result;
	sscanf(buffer,"%d",&result);
	if (result == -1) {
		printf("Error occurred in Acquire_Syscall. Lock%d could not be acquired\n",lockIndex);
		return -1;
	}

   return 1;
	#endif

	#ifndef NETWORK
	//acquire one fo the locks from the lockMap
    acquireSysCallLock->Acquire();
    if(!(lockMap[lockIndex]) || lockIndex<0 || (lockIndex>(maxlock-1))){
		printf("Error acquiring Lock at index %d\n", lockIndex);
		return -1;
	}
	
     
    lockMap[lockIndex]->Acquire(); 
    lockStatus[lockIndex] = 1; //one means it is locked
/* 
	if (dispOption){
	printf("Lock %d : %s acquired by Thread : %s\n", lockIndex, lockNameMap[lockIndex], currentThread->getName());
	}
*/
	acquireSysCallLock->Release();	
	return 1;	
	#endif
}

int Release_Syscall(int lockIndex){
//int Release_Syscall(int lockIndex, int dispOption){
	 #ifdef NETWORK 
		printf("+++++++in Release_Syscall through network+++++++++\n");//100100

 //Send a message to the server 
   PacketHeader outPktHdr, inPktHdr; 
   MailHeader outMailHdr, inMailHdr; 
   char *msg = "4"; 
   char buffer[MaxMailSize]; 
   outPktHdr.to = 0; 
   outMailHdr.to = 0; 
   outMailHdr.from = netname; 
  
   //concatenate strings 
   std::stringstream temp; 
   temp << lockIndex; 
   std::string tempStr; 
   tempStr = temp.str(); 
   strcat(msg, tempStr.c_str()); 
   outMailHdr.length = strlen(msg) +1; 
    
   //Send request "RELEASELOCK" 
   bool success = postOffice->Send(outPktHdr, outMailHdr, msg); 
   if ( !success ) { 
     printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n"); 
     interrupt->Halt(); 
   } 
    
  
   //Wait for the server's response 
   /*postOffice->Receive(netname, &inPktHdr, &inMailHdr, buffer);*/ 
     printf("Lock %d released by server\n", lockIndex); 
   fflush(stdout); 
   return 1;
	#endif 

	#ifndef NETWORK
	releaseSysCallLock->Acquire();
    //release one of the locks from the lockMap
    if(!(lockMap[lockIndex]) || lockIndex<0 || lockIndex>(maxlock-1)){
	printf("Error releasing Lock at index %d\n", lockIndex);
	return -1;
    }

    lockMap[lockIndex]->Release(); 
    lockStatus[lockIndex] = 0; //Zero means it si unlocked
 /*  
	 if (dispOption)
	printf("Lock at index %d : %s released by Thread : %s\n", lockIndex, lockNameMap[lockIndex], currentThread->getName());
	releaseSysCallLock->Release();
*/
	return 1;
#endif
}

int Wait_Syscall(int conditionIndex, int lockIndex){
//int Wait_Syscall(int lockIndex, int conditionIndex){
	 #ifdef NETWORK
	printf("+++++++in Wait_Syscall through network+++++++++\n");//100100

	//Send a message to the server 
   PacketHeader outPktHdr, inPktHdr; 
   MailHeader outMailHdr, inMailHdr; 
   char *msg = "5"; 
   char buffer[MaxMailSize]; 
   outPktHdr.to = 0; 
   outMailHdr.to = 0; 
   outMailHdr.from = netname; 
  
   //concatenate strings 
   std::stringstream temp; 
   temp << conditionIndex; 
   std::string tempStr; 
   tempStr = temp.str(); 
   strcat(msg, tempStr.c_str()); 
   strcat(msg, ","); 
   temp << lockIndex; 
   tempStr = temp.str(); 
   strcat(msg, tempStr.c_str()); 
   outMailHdr.length = strlen(msg) +1; 
  
   //Send request "WAITCV" 
   bool success = postOffice->Send(outPktHdr, outMailHdr, msg); 
   if ( !success ) { 
     printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n"); 
     interrupt->Halt(); 
   } 
   printf("Client %d, waiting on CV %d Lock %d\n",netname,conditionIndex,lockIndex); 
    
   //Wait for the server's response 
   postOffice->Receive(netname, &inPktHdr, &inMailHdr, buffer); 
   printf("Client %d awoken by server\n", netname); 
   fflush(stdout);

   return 1;
   #endif 
		
	#ifndef NETWORK 
	waitSysCallLock->Acquire();
		//wait on a condition variable from the map
	printf("inside of wait");

	if((lockIndex<0))
	{
		printf("Lock index %d is negative\n", lockIndex);
		return -1;
	}
	
	if((conditionIndex < 0))
	{
		printf("Condition index %d is negative\n", conditionIndex);
		return -1;
	}

	if(!(lockMap[lockIndex]) || lockIndex>(maxlock-1))
	{
		printf("Lock does not exist at index %d\n", lockIndex); 
		return -1;
	}

	if(!(conditionMap[conditionIndex]) || conditionIndex>(maxcondition-1))
	{
		printf("Condition variable does not exist at index %d\n", conditionIndex);
		return -1;
	}


    if((lockMap[lockIndex] != NULL) && (conditionMap[conditionIndex] != NULL))
	{
		
		conditionMap[conditionIndex]->Wait(lockMap[lockIndex]);
		printf("Condition variable at index %d  is WAITing on lock at index %d\n",conditionIndex, lockIndex);
		return 1;
	}
	waitSysCallLock->Release();
    return 1;
	#endif 
}


int Signal_Syscall( int conditionIndex, int lockIndex){
//int Signal_Syscall(int lockIndex, int conditionIndex){
	#ifdef NETWORK 
	printf("+++++++in Signal_Syscall through network+++++++++\n");//100100

    //Send a message to the server 
   PacketHeader outPktHdr, inPktHdr; 
   MailHeader outMailHdr, inMailHdr; 
   char *msg = "6"; 
   char buffer[MaxMailSize]; 
   outPktHdr.to = 0; 
   outMailHdr.to = 0; 
   outMailHdr.from = netname; 
  
   //concatenate strings 
   std::stringstream temp; 
   temp << conditionIndex; 
   std::string tempStr; 
   tempStr = temp.str(); 
   strcat(msg, tempStr.c_str()); 
   strcat(msg, ","); 
   temp << lockIndex; 
   tempStr = temp.str(); 
   strcat(msg, tempStr.c_str()); 
   outMailHdr.length = strlen(msg) +1; 
  
   //Send request signal 
   bool success = postOffice->Send(outPktHdr, outMailHdr, msg); 
   if ( !success ) { 
     printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n"); 
     interrupt->Halt(); 
   } 
    
   //Wait for the server's response 
   //postOffice->Receive(netname, &inPktHdr, &inMailHdr, buffer); 
   printf("CV %d Lock %d Signalled", conditionIndex,lockIndex); 
   fflush(stdout); 
   return 1;
 #endif 
		
	
	
	#ifndef NETWORK 
	signalSysCallLock->Acquire();
	//signal on a condition variable from the map
	
    if(!(lockMap[lockIndex]) || lockIndex<0 || !(conditionMap[conditionIndex])|| conditionIndex<0){
	printf("Error: Condition variable at index %d cannot SIGNAL lock %d\n", conditionIndex, lockIndex);
	
	return -1;
    }
    if(lockStatus[lockIndex] != 1){	
	
	printf("Condition variable at index %d SIGNALs lock at index %d, but The lock it has not been acquired yet\n", conditionIndex, lockIndex);
	conditionMap[conditionIndex]->Signal(lockMap[lockIndex]);	
	return -1;
	}

    if((lockMap[lockIndex] != NULL) && (conditionMap[conditionIndex] != NULL)){
	conditionMap[conditionIndex]->Signal(lockMap[lockIndex]);
	printf("Condition variable at index %d  is SIGNALling on lock at index %d\n",conditionIndex, lockIndex);
    }
	signalSysCallLock->Release();
    return 1;
	#endif 

}

int Broadcast_Syscall( int conditionIndex, int lockIndex){
	#ifdef NETWORK 
	printf("+++++++in Broadcast_Syscall through network+++++++++\n");//100100

 //Send a message to the server 
   PacketHeader outPktHdr, inPktHdr; 
   MailHeader outMailHdr, inMailHdr; 
   char *msg = "7"; 
   char buffer[MaxMailSize]; 
   outPktHdr.to = 0; 
   outMailHdr.to = 0; 
   outMailHdr.from = netname; 
  
   //concatenate strings 
   std::stringstream temp; 
   temp << conditionIndex; 
   std::string tempStr; 
   tempStr = temp.str(); 
   strcat(msg, tempStr.c_str()); 
   strcat(msg, ","); 
   temp << lockIndex; 
   tempStr = temp.str(); 
   strcat(msg, tempStr.c_str()); 
   outMailHdr.length = strlen(msg) +1; 
  
   //Send request "BROADCASTCV" 
   bool success = postOffice->Send(outPktHdr, outMailHdr, msg); 
   if ( !success ) { 
     printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n"); 
     interrupt->Halt(); 
   }    
  
   //Wait for the server's response 
   //postOffice->Receive(netname, &inPktHdr, &inMailHdr, buffer); 
   printf("CV %d Lock %dBroadcasted\n", conditionIndex,lockIndex); 
   fflush(stdout); 	
	return 1;
 #endif 

	//Broadcast on a condition variable from the map
	#ifndef NETWORK 
	broadcastSysCallLock->Acquire();

    if(!(lockMap[lockIndex]) || lockIndex<0 || !(conditionMap[conditionIndex]) || conditionIndex<0 ){
	printf("Error: Condition variable at index %d cannot BROADCAST lock at index %d\n", conditionIndex, lockIndex);
	return -1;
    }
    if(lockStatus[lockIndex] != 1){	
	
	printf("Condition variable at index %d BROADCASTs lock at index %d, but The lock it has not been acquired yet\n", conditionIndex, lockIndex);
	conditionMap[conditionIndex]->Broadcast(lockMap[lockIndex]);	
	return -1;
	}
    if((lockMap[lockIndex] != NULL) && (conditionMap[conditionIndex] != NULL)){
	conditionMap[conditionIndex]->Broadcast(lockMap[lockIndex]);
	printf("Condition variable at index %d is BROADCASTing on lock at index %d\n",conditionIndex,lockIndex);
    }

	broadcastSysCallLock->Release();
    return 1;
	#endif 
}

int DestroyLock_Syscall(int lockIndex){
#ifdef NETWORK

	printf("+++++++in DestroyLock_Syscall through network+++++++++\n");//100100

	//send a message to the server 
   PacketHeader outPktHdr, inPktHdr; 
   MailHeader outMailHdr, inMailHdr; 
   char *msg = "8"; 
   char buffer[MaxMailSize]; 
   outPktHdr.to = 0; 
   outMailHdr.to = 0; 
   outMailHdr.from = netname; 
  
   //concatenate strings 
   std::stringstream temp; 
   temp << lockIndex; 
   std::string tempStr; 
   tempStr = temp.str(); 

   printf("****************tempStr: %s\n", tempStr);//100100

   strcat(msg, tempStr.c_str()); 
   outMailHdr.length = strlen(msg) +1; 

   //send request destorylock 
   bool success = postOffice->Send(outPktHdr, outMailHdr, msg); 
   if ( !success ) { 
     printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n"); 
     interrupt->Halt(); 
   } 
    
   //Wait for the server's response 
   //postOffice->Receive(netname, &inPktHdr, &inMailHdr, buffer); 
   printf("Lock %d deleted", lockIndex); 
   fflush(stdout); 
   return 1;
/*
	postOffice->Receive(postOffice->machineID(), &Msg.inPktHdr, &Msg.inMailHdr, Msg.inData);
	fflush(stdout);

	
	int result;
	sscanf(Msg.inData,"%d",&result);
	if (result == -1) {
		DEBUG('1',"Could not delete Lock '%d'\n", index);
		return -1;
	}
*/
#endif

#ifndef NETWORK
	destroyLockSysCallLock->Acquire();
    //DestroyLock system call
    //removing lock from the lock map
    	
    if(!(lockMap[lockIndex]) || lockIndex<0 || (lockIndex>(maxlock-1))){
	printf("Error destroying lock - wrong index\n");
        return -1;  
    }
    else{
	lockMap[lockIndex] = NULL;
	printf("Lock at index %d was deleted\n", lockIndex);
	destroyLockSysCallLock->Release();
        return lockIndex;
    } 
	#endif

}

int DestroyCondition_Syscall(int conditionIndex){
	destroyConditionSysCallLock->Acquire();
    //DestroyCondition system call
    //removing condition from the condition map
    	
    if(!(conditionMap[conditionIndex]) || conditionIndex<0 || (conditionIndex>(maxcondition-1))){
	printf("Error destroying condition - wrong index\n");
        return -1;  
    }

    else{
	conditionMap[conditionIndex] = NULL;
	printf("Condition %d was deleted\n", conditionIndex);
	destroyConditionSysCallLock->Release();
        return conditionIndex;
    } 	
}


/*  Project 3 updates Locks and CVs end*/

int GetInputInt_Syscall(int threadDesc, int len)
{
	char *buf = new char[32];
	int returnValue = 0;
	
	if( copyin(threadDesc,len,buf) == -1 ) 
	{
		printf("%s","Bad pointer passed to Create\n");
		delete buf;
		return -1;
  }
  buf[len]='\0';
  cout<<buf<<" : "<<endl;
  cin >> returnValue;
  return returnValue;
}

int WriteOutputInt_Syscall(unsigned int vaddr, int len, int outputValue)
{
	char *buf;		// Kernel buffer for output
   
  if ( !(buf = new char[len]) ) 
  {
		printf("%s","Error allocating kernel buffer for write!\n");
		return -1;
  } 
  else 
  {
  		if ( copyin(vaddr,len,buf) == -1 ) 
  		{
	    	printf("%s","Bad pointer passed to to write: data not written\n");
	    	delete[] buf;
	    	return -1;
			}
  }
		
	buf[len] = '\0';
 
  for (int ii=0; ii<len; ii++) 
  {
				printf("%c",buf[ii]);
  } 
	printf(" : %d\n", outputValue);
	
	delete[] buf;
	return 1;
}

int WriteOutput2Int_Syscall(unsigned int vaddr, int len, int outputValue1, int outputValue2)
{
	char *buf;		// Kernel buffer for output
   
  if ( !(buf = new char[len]) ) 
  {
		printf("%s","Error allocating kernel buffer for write!\n");
		return -1;
  } 
  else 
  {
  		if ( copyin(vaddr,len,buf) == -1 ) 
  		{
	    	printf("%s","Bad pointer passed to to write: data not written\n");
	    	delete[] buf;
	    	return -1;
			}
  }
		
	buf[len] = '\0';
 
  for (int ii=0; ii<len; ii++) 
  {
				printf("%c",buf[ii]);
  } 
	printf(" : %d - %d\n", outputValue1, outputValue2);
	
	delete[] buf;
	return 1;
}

int GetCurThreadName_Syscall(int res_p)
{	
	return currentThread->getName()[0];
}

int GetCurThreadID_Syscall(int dummyVar)
{
	return currentThread->nThreadID;
}


int Exit_Syscall(int threadIndex)
{
	ProcessTableLock->Acquire();
	printf("Exit index: %d\n", threadIndex);
	myProcessTable[currentThread->space->AddressSpaceIndex].sthreadTable[currentThread->nThreadID].threadStatus = false; //now status of curr thread is false 
	
	
		// check if last thread of process table 
		if (--myProcessTable[currentThread->space->AddressSpaceIndex].activeThreads == 0)
		{
			currentThread->space->DeallocateProcessTable(currentThread->space->AddressSpaceIndex);
			for(int i; i<NumPhysPages; i++){
			if( currentThread->space->getID() == IPT[i].ProcessID ){
				resetIPT(i);//invalidating ipt on exit
			}
			}
		}
		
		// check if last active thread in the process 
		if(--activeThreads == -1)
		{
			ProcessTableLock->Release();
			interrupt->Halt();
		}
	
	ProcessTableLock->Release();
	currentThread->Finish();
	
	return 1;
}

void forkThreadFunction(unsigned int funcThreadHandlerAddr)
{
	currentThread->space->InitRegisters();
	machine->WriteRegister(PCReg, funcThreadHandlerAddr);
	machine->WriteRegister(NextPCReg, (funcThreadHandlerAddr+4));
	machine->WriteRegister(StackReg, currentThread->topOfStack);
	currentThread->space->RestoreState();
	machine->Run();
	
}

int Fork_Syscall(int funcThreadHandlerAddr, int threadName, int len)
{
	ProcessTableLock->Acquire();
	char *buf = new char[len];
	int currentProcess = currentThread->space->AddressSpaceIndex;
	int totalthreads = myProcessTable[currentProcess].totalThreads;

	if( copyin(threadName,len,buf) == -1 ) 
	{
		printf("%s","Bad pointer was passed\n");
		delete buf;
		return -1;
  	}
	
	
	// if max number of threads is exceeding	
	if (totalthreads >= (MAX_NUM_THREADS-1))
	{
		printf("Exceeds Maximum Number of Threads\n");
		ProcessTableLock->Release();
		return -1;
	}
	
	len = 32 + len;
	
	// used to fork a new kernel thread 
	Thread *tempThread;
	char *threadDesc = new char[len];
	
	// Increment number of existing threads in the process table
	++myProcessTable[currentThread->space->AddressSpaceIndex].activeThreads;
	
	// keeping track of existing threads 
	int nThreadID = ++myProcessTable[currentProcess].totalThreads;
	
	tempThread = new Thread(threadDesc);
	tempThread->nThreadID = nThreadID;
	
					
	// allocating stack space to a new thread 
	myProcessTable[currentProcess].topOfStack += UserStackSize;
				
	// setting top of stack 
	tempThread->topOfStack = myProcessTable[currentProcess].topOfStack;
	
	// populating thread table 
	ThreadTable *tempThreadTable;
	tempThreadTable = new ThreadTable;
	
	// filing table
	tempThreadTable->threadStatus = true;
	tempThreadTable->topOfStack = tempThread->topOfStack;
	tempThreadTable->ProcessID = currentProcess;
	
	// assign thread table to the current process
	myProcessTable[currentProcess].sthreadTable[nThreadID] = *tempThreadTable;
		
	tempThread->space =  currentThread->space;
	
	// on fork we update page table 
	currentThread->space->UpdatePageTableOnFORK(currentProcess);
		
	// kernel level forking 	
	tempThread->Fork((VoidFunctionPtr)forkThreadFunction, funcThreadHandlerAddr);
	activeThreads++;
	totalThreads++;
		
	ProcessTableLock->Release();
	return nThreadID;
}

void ExecThread_Handler(int funcExecHandlerAddr)
{
	currentThread->space->InitRegisters();
	currentThread->space->RestoreState();
	machine->Run();
}

int Exec_Syscall(unsigned int funcExecHandlerAddr)
{
	lockExec->Acquire();
	int len = 100;
 
	if (userProcesses >= (MAX_NUM_PROCESSES-1))
	{
		printf("Exceeds max number of processes\n");
		lockExec->Release();
		return -1;
	}
	
	int numProcessID = ++userProcesses;
	
	ProcessTableLock->Acquire();
	myProcessTable[numProcessID].sthreadTable[0].threadStatus = true;
	ProcessTableLock->Release();
	
	char *filename = new char[len+1];
	OpenFile *executable;
  
  if( copyin(funcExecHandlerAddr,len,filename) == -1 ){
	printf("%s"," Bad pointer passed to Open\n");
	delete[] filename;
	lockExec->Release();
	return -1;
  }
  filename[len]='\0';
  executable = fileSystem->Open(filename); //open executable
	if (executable == NULL){
		lockExec->Release();
		return -1;
        }


  AddrSpace *space;
  //create new address space
  space = new AddrSpace(executable);

	ProcessTableLock->Acquire();
	myProcessTable[numProcessID].sthreadTable[0].topOfStack = myProcessTable[numProcessID].topOfStack; 
	ProcessTableLock->Release();

  Thread *tempThread;
  char *tname = new char[32];
	sprintf(tname,"Process : %d",numProcessID);
  //create new thread
  tempThread = new Thread(tname);        
  tempThread->nThreadID = 0;
  tempThread->space = space;
#ifndef USE_TLB
	delete executable;
#endif	

	tempThread->topOfStack = myProcessTable[numProcessID].topOfStack;
	machine->WriteRegister(2,numProcessID);
	//forking this thread
	tempThread->Fork((VoidFunctionPtr)ExecThread_Handler, funcExecHandlerAddr);
	//incrementing counters
	totalThreads++;
	activeThreads++;
	lockExec->Release();
	return numProcessID;
}


#endif

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall

  if(which == PageFaultException){
    DEBUG('a',"PageFaultException\n");
    stats->numPageFaults++;
    HandlePageFault(machine->ReadRegister(BadVAddrReg));
    return;
  }


    if ( which == SyscallException ) {
	switch (type) {
	    default:
		DEBUG('a', "Unknown syscall - shutting down.\n");
	    case SC_Halt:
		DEBUG('a', "Shutdown, initiated by user program.\n");
		interrupt->Halt();
		break;
	    case SC_Create:
		DEBUG('a', "Create syscall.\n");
		Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Open:
		DEBUG('a', "Open syscall.\n");
		rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Write:
		DEBUG('a', "Write syscall.\n");
		Write_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Read:
		DEBUG('a', "Read syscall.\n");
		rv = Read_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Close:
		DEBUG('a', "Close syscall.\n");
		Close_Syscall(machine->ReadRegister(4));
		break;
	    
#ifdef CHANGED

	  case SC_CreateLock:
		DEBUG('a', "CreateLock syscall.\n");
		rv = CreateLock_Syscall(machine->ReadRegister(4));
		break;
		
		case SC_CreateCondition:
		DEBUG('a', "CreateCondition syscall.\n");
		rv = CreateCondition_Syscall(machine->ReadRegister(4));
		break;
		
		case SC_DestroyLock:
		DEBUG('a', "DestroyLock syscall.\n");
		rv = DestroyLock_Syscall(machine->ReadRegister(4));
		break;
		
		case SC_DestroyCondition:
		DEBUG('a', "DestroyCondition syscall.\n");
		rv = DestroyCondition_Syscall(machine->ReadRegister(4));
		break;
		
		case SC_Acquire:
		DEBUG('a', "Acquire syscall.\n");
		rv = Acquire_Syscall(machine->ReadRegister(4));
		break;	

	    case SC_Release:
		DEBUG('a', "Release syscall.\n");
		rv = Release_Syscall(machine->ReadRegister(4));
		break;
	    case SC_Wait:
		DEBUG('a', "Wait syscall.\n");
		rv = Wait_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
		break;
	    case SC_Signal:
		DEBUG('a', "Signal syscall.\n");
		rv = Signal_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
		break;
	    case SC_Broadcast:
		DEBUG('a', "Broadcast syscall.\n");
		rv = Broadcast_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
		break;
		
		case SC_Exit:
		DEBUG('a', "ExitThread syscall.\n");
		rv = Exit_Syscall(machine->ReadRegister(4));
		break;
		
		
		case SC_Yield:
		DEBUG('a', "Yield syscall.\n");
		Yield_Syscall(machine->ReadRegister(4));
		break;	
		
		case SC_Fork:
		DEBUG('a', "Fork syscall.\n");
		rv = Fork_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
		break;	
		
		case SC_Exec:
		DEBUG('a', "Exec syscall.\n");
		rv = Exec_Syscall(machine->ReadRegister(4));
		break;
		
		case SC_GetInputInt:
		DEBUG('a', "GetInputInt syscall.\n");
		rv = GetInputInt_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;	
		
		case SC_WriteOutputInt:
		DEBUG('a', "WriteOutputInt syscall.\n");
		rv = WriteOutputInt_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
		break;
		
		case SC_WriteOutput2Int:
		DEBUG('a', "WriteOutput2Int syscall.\n");
		rv = WriteOutput2Int_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6), machine->ReadRegister(7));
		break;	
		
		case SC_GetCurThreadName:
		DEBUG('a', "GetCurThreadName syscall.\n");
		rv = GetCurThreadName_Syscall(machine->ReadRegister(4));
		break;
		
		case SC_GetCurThreadID:
		DEBUG('a', "GetCurThreadID syscall.\n");
		rv = GetCurThreadID_Syscall(machine->ReadRegister(4));
		break;
#endif
	}

	// Put in the return value and increment the PC
	machine->WriteRegister(2,rv);
	machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
	machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
	machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
	return;
    } else {
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
	printf("reg 39 %d\n",machine->ReadRegister(39));
      interrupt->Halt();
    }
}
